﻿
#pragma once

#include "Lib/qwebdavlib/qwebdav.h"
#include "Lib/qwebdavlib/qwebdavdirparser.h"
#include "Lib/qwebdavlib/qwebdavitem.h"
#include "DirMonitor/DirMonitor.h"
#include "DirMonitor/DirMonitorForWin.h"
#include "DirMonitor/DirMonitorForLinux.h"

#include <QObject>
#include <QDebug>
#include <QDir>
#include <QProcess>
#include <QApplication>
#include <QNetworkAccessManager>
#include <QBuffer>
#include <QNetworkReply>
#include <QAuthenticator>

const QString SyncCfgDirRelativePath = ".Sync";
const QString FilePathListCfgFilePath = SyncCfgDirRelativePath + "/List.txt";

class WorkerModel : public QObject
{
    Q_OBJECT
public:
    enum ErrorType {
        NoError = 0,
        UpdateHostsInfoToSysFailed,
        DbOpenFailed,
        SqlExecFailed,
        AccountIsExist,
        AccountExistError,
        AccountNotExist,
        NotFindDnfExeFile,
        CheckOldPwdFailed,
        CheckPwdFailed,
        AccountNameIsEmpty,
    };
    struct Error {
        ErrorType Type = ErrorType::NoError;
        QString Msg;
    };

    struct WebdavCfg {
        QWebdav::QWebdavConnectionType ConnType;
        QString HostName;
        QString RootPath;
        QString UserName;
        QString Password;
        int Port;
        QString LocalDirPath;
        QString RemoteDirPath;
        WebdavCfg() {
            ConnType = QWebdav::QWebdavConnectionType::HTTP;
            Port = 1;
        }
    };

    enum TaskType {
        Upload,
        Download,
        Rm
    };

    struct Task {
        TaskType Type;
        QNetworkReply *ReplyPtr;
        QString LocalFilePath;
        QFile *LocalFilePtr;
        QString SrvFilePath;
        QDateTime FileLastModifiedTime;
        Task() {
            this->Type = Upload;
            this->ReplyPtr = nullptr;
            this->LocalFilePtr = nullptr;
        }

        bool operator==(const Task &other) {
            return (this->Type == other.Type
                    && this->LocalFilePath == other.LocalFilePath
                    && this->SrvFilePath == other.SrvFilePath
                    );
        }
    };
    struct ChangeTimeInfo {
        QString Path;
        QDateTime LastModifiedTime;
        bool Done;

        ChangeTimeInfo() {
            Done = false;
        }
    };

    enum State {
        Normal = 0,
        Busy
    };

    explicit WorkerModel(QObject *parent = nullptr);
    ~WorkerModel();

    void Init(const WebdavCfg &cfg);
    void SetId(int id);
    int GetId();

Q_SIGNALS:
    void SendError(Error err);
    void SetWebdavCfgFinished(bool succeed, const WebdavCfg &cfg);
    void StateChanged(State s);

public Q_SLOTS:
    void OnReqSetWebdavCfg(int threadId, const WebdavCfg &cfg);

private slots:
    void onWebdavAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
    void onWebdavErrorChanged(QString errorMsg);
    void onDirParserErrorChanged(QString errorMsg);
    void onDirParserFinished();
    void onReplyUploadProgressChanged(Task *task, qint64 bytesSent, qint64 bytesTotal);
    void onReplyFinished(Task *task);
    void onReplyError(Task *task, QNetworkReply::NetworkError error);
    void onReplyDownloadProgressChanged(Task *task, qint64 bytesSent, qint64 bytesTotal);
    // void onLocalFileChanged(const QString &path);
    // void onLocalDirChanged(const QString &dirPath);
    void onLocalFileClosed(const QString &path);
    void onLocalFileMovedOut(const QString &path);
    void onLocalFileMovedIn(const QString &path);
    void onLocalFileCreated(const QString &path);
    void onLocalFileDeleted(const QString &path);

private:
    bool setWebdavCfg(const WorkerModel::WebdavCfg &cfg);
    void initialAsync();
    void printError(QString errorMsg);
    void asyncUploadFile(Task *task, const QString &path);
    void asyncUploadDir(const QString &dirPath);
    QStringList getDirAllFilePathList(const QString &path);
    void syncDownloadFile(const QString &srvFilePath);
    void asyncDownloadFile(Task *task, const QString &srvFilePath);
    void asyncRmSrvFile(Task *task, const QString &path);
    QWebdavItem syncGetSrvFileInfo(const QString &srvFilePath);
    bool isSrvFileSameToLocalFile(const QWebdavItem &item, const QString &localFilePath);
    void deleteLocalFileOrDir(const QString &path);
    void asyncUploadPathListCfg(const QList<QString> &pathList);
    void addTask(TaskType type, const QString &localFilePath, const QString &srvFilePath);
    void dispathTask();

private:
    int m_id;
    bool m_isInited;
    DirMonitor *m_localDirMonitor;
    WebdavCfg m_webdavCfg;

    QWebdav *m_webdav;
    QWebdavDirParser *m_webdavDirParser;
    QDir m_localDir;
    QDir m_srvDir;
    QNetworkReply *m_authenicatedReply;
    QList<QString> m_localFilePathList;

    QList<Task *> m_taskList;
    QList<Task *> m_runningTaskList;
    int m_runningTaskMaxCount;
    QMap<QString, QWebdavItem> m_mapOfPathToWebdavItem;
    QList<ChangeTimeInfo> m_changeTimeInfoList;
    int m_loopCount;
    State m_state;
};
